// 交叉类型 T & U  与的操作
const mergeFunc = <T, U>(arg1: T, arg2: U): T & U => {
  let res = {} as T & U
  res = Object.assign(arg1, arg2)
  return res
}

mergeFunc({a: 'a'}, {b: 'b'})

// 联合类型  number | string  或的操作

const getLengthFunc = (content: string | number): number => {
  if (typeof content === 'string') {
    return content.length
  } else {
    return content.toString().length
  }
}


const valueList = [123, 'abc']
const getRandomValue = () => {
  const number = Math.random() * 10
  if (number < 5) {
    return valueList[0]
  } else {
    return valueList[1]
  }
}

const item = getRandomValue()
console.log(item);

// 类型保护
// 如果是类型复杂的用函数
function isString(value: number | string): value is string {
  return typeof value === 'string'
}

// 简单的可以直接用 typeof 条件一： === 或者 !== , 
//条件二： string/number/boolean/symbol 中的一种

if (typeof item === 'string') {
  console.log(item.length);
} else {
  console.log(item.toFixed());
}


class CreatedByClass1 {
  public age = 18
  constructor() {}
}

class CreatedByClass2 {
  public name = 'lison'
  constructor() {}
}

function getRandomItem() {
  return Math.random() < 0.5 ? new CreatedByClass1() : new CreatedByClass2
}

const item1 = getRandomItem()

if (item1 instanceof CreatedByClass1) {
  console.log(item1.age);  
} else {
  console.log(item1.name);  
}


// null/undefined

// 类型断言  !

function getSplicesStr(num: number | null): string {
  function getRes(prefix: string) {
    return prefix + num!.toFixed().toString()  // num!.toFixed()   num的值一定不为null
  }
  num = num || 0.1
  return getRes('lison')
}

// 类型别名
type TypeString = string


// 字面量类型
type Name = 'Lison'
type Direction = 'north' | 'east' | 'south' | 'west'
function getDirectionFirstLetter(direction: Direction) {
  return direction.substr(0, 1)
} 

console.log(getDirectionFirstLetter('east'));

type Age = 18

interface InfoInterfaces {
  name: string;
  age: Age
}

const _info: InfoInterfaces = {
  name: 'lison',
  age: 18
}


/**
 * 可辨识联合两要素
 * 1.具有普通的单例类型属性
 * 2.一个类型别名包含了哪些类型的联合
 */

interface Square {
  kind: 'square';
  size: number;
}

interface Rectangle {
  kind : 'rectangle';
  height: number;
  width: number;
}

interface Circle {
  kind: 'circle';
  radius: number;
}


type Shape = Square | Rectangle | Circle

function assertNever(value: never): never {
  throw new Error('Unexpected object: ' + value)
}

function getArea(s: Shape): number {
  switch (s.kind) {
    case 'square': return s.size * s.size;
    case 'rectangle': return s.height * s.width;
    case 'circle': return Math.PI * s.radius ** 2
    default: return assertNever(s)
  }
}

